package edu.vanderbilt.vshare.multicast;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

/**
 * class that allows for submitting work orders or
 * accessing file info, rates, etc. from active server threads
 * 
 * @author jedmondson
 *
 */

public class ServerThreadRepo {

	/**
	 * add a multicasting thread that will send data on an existing fileid
	 * at some data rate
	 * 
	 * @param     ip                 multicast ip to send on
	 * @param     fileid             id of file
	 * @param     rate               data rate
	 */
	
	public void add (String ip, int fileid, String rate)
	{
		int encodedIp = Utility.ipToInt(ip);
		MulticastSenderThread new_thread = new MulticastSenderThread (ip, fileid, rate);
		
		SharedGlobals.threadPool_.execute(new_thread);
		
		// provide thread lookup for this multicast ip
		thread_map.put(encodedIp, new_thread);
		
		// provide ip lookup for a rate and file id
		ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer> ();
		map.put(rate, encodedIp);
		
		fileid_map.put(fileid, map);
	}

	/**
	 * add a multicasting thread that will send data on an existing fileid
	 * at some data rate
	 * 
	 * @param     ip                 multicast ip to send on
	 * @param     filename           name of file
	 * @param     rate               data rate
	 */
	
	public void add (String ip, String filename, String rate)
	{
		if (SharedGlobals.store.contains(filename))
		{
			int fileid = SharedGlobals.store.getFileId(filename);
		
			add(ip,fileid,rate);
		}
	}

	/**
	 * add a work load to send to existing threads. Note that this will add the
	 * work load to ALL existing threads that are multicasting the particular file 
	 * 
	 * @param     fileid             id of file (not currently used for
	 *                               anything but verification)
	 * @param     first              first packet index to send (-1 for meta)
	 * @param     number             number of packets to send starting with first
	 * @return                       true if work was added
	 */
	
	public boolean add (int fileid, int first, int number)
	{
		if (fileid_map.containsKey(fileid))
		{
			ConcurrentHashMap<String, Integer> map = fileid_map.get(fileid);
			
			Collection<Integer> ips = map.values();
			
			for (Integer ip: ips)
			{
				add (ip, fileid, first, number);
			}
		}
		
		return false;
	}

	/**
	 * add a work load to send to an existing thread. See the Utility class
	 * for some handy functions for converting ip addresses to encoded form
	 * 
	 * @param     encoded_ip         integer format of ip address to send to
	 * @param     fileid             id of file (not currently used for
	 *                               anything but verification)
	 * @param     first              first packet index to send (-1 for meta)
	 * @param     number             number of packets to send starting with first
	 * @return                       true if work was added
	 * @see Utility
	 */
	
	public boolean add (int encoded_ip, int fileid, int first, int number)
	{		
		if (thread_map.containsKey(encoded_ip))
		{
			MulticastSenderThread thread = thread_map.get(encoded_ip);
			if (thread.getFileid() == fileid)
			{
				// This section has been considerably cleaned up. The semantics
				// should now be the following:
				// first must be within range of -1 to to numPackets - 1
				// if number is negative, send from first to end
				// if number is positive, send number packets starting with first
				
				// note that this means we can request metadata and all packets in
				// 1 request
				
				if (first < 0)
					first = -1;
				
				int numPackets = thread.getPackets(fileid);
				
				if (number < 0)
				{
					thread.add(first, numPackets - first - 1);
				}
				// else just add the number
				else
				{
					thread.add(first, number);
				}

				return true;
			}
		}
		
		return false;
	}
	

	/**
	 * Retrive the metadata for the file. If -1 is provided, return
	 * the file repo meta data.
	 * 
	 * @param     encoded_ip         id of the file to check on
	 * @return                   metadata for the file store
	 */
	
	public byte [] getMeta (int encoded_ip)
	{
		if(thread_map.containsKey (encoded_ip))
		{
			return thread_map.get(encoded_ip).getMeta();
		}
		
		// user is looking for file repo information
		if(encoded_ip == -1)
		{
			if (meta != null)
				return meta;
			
			Iterator<Integer> i = thread_map.keySet ().iterator ();
			Integer key = null;
			MulticastSenderThread value = null;
			StringBuilder builder = new StringBuilder ();
			
			builder.append ("<addresses>\n");
			
			while (i.hasNext ())
			{
				key = (Integer)i.next ();
				
				value = thread_map.get (key);
				
				builder.append (new String (value.getMeta ()));
			}
			
			builder.append ("</addresses>\n");

			// note that this is NOT aligned to a packet size
			meta = builder.toString().getBytes ();
			
			return meta;
		}
		
		// fileid was unrecognized
		return null;
	}
	
	private HashMap <Integer, MulticastSenderThread> thread_map = 
		                    new HashMap <Integer, MulticastSenderThread> (); 
	private ConcurrentHashMap <Integer, ConcurrentHashMap<String, Integer> > fileid_map = 
		       new ConcurrentHashMap<Integer, ConcurrentHashMap<String, Integer> > ();
	byte [] meta = null;
}
